package mapper.context;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import mapper.base.IPlugin;
import mapper.config.MapperApiConfigure;
import mapper.model.MappingInfo;
import mapper.model.PluginInfo;
import mapper.model.PluginMappingInfo;
import mapper.model.ResponseData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

@Slf4j
@Component
public class PluginContext {

    @Autowired
    private MapperApiConfigure mapperApiConfigure;

    @Value("${mapper-api.context:}")
    private String context;

    private Map<RequestMethod, TreeSet<PluginMappingInfo>> pluginMap = new HashMap<>();
    private List<String> pluginNames = new ArrayList<>();
    private Comparator comparator = new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof PluginInfo && o2 instanceof PluginInfo) {
                PluginInfo e1 = (PluginInfo) o1;
                PluginInfo e2 = (PluginInfo) o2;
                return e1.getOrder() - e2.getOrder();

            }
            throw new RuntimeException("传入数据错误！");
        }
    };

    public void register(PluginInfo pluginInfo, IPlugin plugin) {
        pluginInfo.getMethods().forEach(method-> {
            if(pluginMap.containsKey(method)) {
                return;
            }
            if(pluginNames.contains(pluginInfo.getName())) {
                return;
            }
            pluginMap.put(method, new TreeSet<>(comparator));
            pluginMap.get(method).add(new PluginMappingInfo() {{
                setPlugin(plugin);
                setMethods(pluginInfo.getMethods());
                setClassloaderPath(pluginInfo.getClassloaderPath());
                setName(pluginInfo.getName());
                setOrder(pluginInfo.getOrder());
            }});
            pluginNames.add(pluginInfo.getName());
        });
    }

    private String getUrl(MappingInfo mappingInfo) {
        String url = mappingInfo.getUrl();
        if(context!=null && !"".equals(context.trim())) {
            url = joinUrl(context, url);
        }

        return url;
    }

    private String joinUrl(String... path) {
        String url = "";
        for(String temp : path) {
            if(temp==null || "".equals(context.trim())) {
                continue;
            }

            if(temp.startsWith("/")) {
                temp = temp.substring(1);
            }
            if(temp.endsWith("/")) {
                temp = temp.substring(0, temp.length()-2);
            }
            url = String.format("%s/%s", url, temp);
        }
        if(url.startsWith("/")) {
            return url;
        }
        return String.format("/%s", url);
    }

    public ResponseEntity executePlugins(
            Map<String, String> pathVar, 
            Map<String, Object> param, 
            Map<String, Object> data, 
            MultipartFile file,
            Map<String, String> extendParam,
            ResponseData responseData, 
            HttpServletRequest request, HttpServletResponse response) {
        ResponseEntity result = new ResponseEntity(new ResponseData(500, "ERR", data), HttpStatus.INTERNAL_SERVER_ERROR);
        Set<PluginMappingInfo> setter = pluginMap.get(RequestMethod.valueOf(request.getMethod()));
        Iterator<PluginMappingInfo> it = setter.iterator();
        while (it.hasNext()) {
            PluginMappingInfo pluginInfo = it.next();
            try {
                Optional<MappingInfo> opts = mapperApiConfigure.getMappers().stream().filter(a -> getUrl(a).equals(request.getRequestURI())).findFirst();
                if(opts.isPresent() && !opts.get().getIgnore().contains(pluginInfo.getName())) {
                    result = pluginInfo.getPlugin().execute(pathVar, param, data, file, opts.get().getExtend(), responseData, request, response);
                }
            } catch (Throwable throwable) {
                log.error("Execute plugin error------------> ", throwable);
            }
        }
        return result;
    }
}
